#include "Multicast.h"


int family2proto(int family)
{
    switch (family) {
        case AF_INET: {
            return (IPPROTO_IP);
        }
#ifdef    IPV6
        case AF_INET6: {
            return (IPPROTO_IPV6);
        }
#endif
        default: {
            return (-1);
        }
    }
}

int mcast_join(int sockfd, const SA* grp, socklen_t grplen, const char* ifname, u_int ifindex)
{
/**
 * @brief 不限定协议加入多播组
 * 
 */
#ifdef MCAST_JOIN_GROUP
    struct group_req req;
    if (ifindex > 0)
    {
        req.gr_interface = ifindex;
    }
    else if (ifname != NULL)
    {
        if ((req.gr_interface = if_nametoindex(ifname)) == 0)
        {
            errno = ENXIO;
            return -1;
        }
    }
    else
    {
        req.gr_interface = 0;
    }

    if (grplen > sizeof(req.gr_group))
    {
        errno = EINVAL;
        return -1;
    }
    memcpy(&req.gr_group, grp, grplen);
    return (setsockopt(sockfd, family2proto(grp->sa_family), MCAST_JOIN_GROUP, &req, sizeof(req)));

/**
 * @brief IPV4版本加入多播组
 * 
 */
#else
    switch (grp->sa_family)
    {
    case AF_INET:
        {
            struct ip_mreq mreq;
            struct ifreq ifr;
            memcpy(&mreq.imr_multiaddr, &((const struct sockaddr_in*)grp)->sin_addr, sizeof(struct in_addr));
            if(ifindex > 0)
            {
                if(if_indextoname(ifindex, ifr.ifr_name) == NULL)//通过索引值获取网卡名称
                {
                    errno = ENXIO;
                    return -1;
                }
                goto doioctl;
            }
            else if(ifname != NULL)
            {
                /* 通过网卡名称获取网卡的IP */
                strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
                doioctl:
                #if DEMO_DEBUG
                    if (ioctl(sockfd, SIOCGIFADDR, &ifr)< 0)
                    {
                        return -1;
                    }
                    memcpy(&mreq.imr_interface, &((struct sockaddr_in*)&ifr.ifr_addr)->sin_addr, sizeof(struct in_addr));//获取指定网卡IP
                #else
                    mreq.imr_interface = 0;
                #endif
            }
            else
            {
                mreq.imr_interface.s_addr = htonl(INADDR_ANY);
            }
            // return (setsockopt(sockfd, SOL_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)));//SOL_IP与IPPROTO_IP的区别？
            return (setsockopt(sockfd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &mreq, sizeof(mreq)));//加入多播组
        }
        break;
#ifdef IPV6

#endif
    default:
        errno = EAFNOSUPPORT;
        break;
    }
#endif
    return -1;
}

int getfamily(int sockfd)
{
    struct sockaddr_storage ss;
    socklen_t len = sizeof(ss);

    if (getsockname(sockfd, (SA*)&ss, &len) < 0)
    {
        return -1;
    }
    return ss.ss_family;
}

int mcast_setloop(int sockfd, int flag)
{
    switch (getfamily(sockfd))
    {
    case AF_INET:
        return (setsockopt(sockfd, IPPROTO_IP, IP_MULTICAST_LOOP, &flag, sizeof(flag)));
        break;
#ifdef  IPV6

#endif
    default:
        errno = EAFNOSUPPORT;
        break;
    }
    return -1;
}


#include <sys/utsname.h>
#define     SENDRATE        5

void send_all(int sendfd, SA* sadest, socklen_t salen)
{
    char line[MAXLEN];
    struct utsname myname;
    if (uname(&myname) < 0)
    {
        printf("uname error\n");
        return ;
    }
    snprintf(line, sizeof(line), "%s,%d", myname.nodename, getpid());
    for (; ;)
    {
        printf("send :%s\n", line);
        display_sockaddr(sadest);
        sendto(sendfd, line, strlen(line), 0, sadest, salen);
        sleep(SENDRATE);
    }
}


void recv_all(int recvfd, socklen_t salen)
{
    char line[MAXLEN+1];
    struct sockaddr* safrom = (struct sockaddr*)malloc(salen);
    for(;;)
    {
        int n = recvfrom(recvfd, line, MAXLEN, 0, safrom, &salen);
        line[n] = 0;
        printf("recv :%s\n", line);
        display_sockaddr(safrom);
    }
}